home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / stevie.arc / SEARCH.C < prev    next >
Text File  |  1990-01-10  |  14KB  |  718 lines

  1. /*
  2.  * STevie - ST editor for VI enthusiasts.    ...Tim Thompson...twitch!tjt...
  3.  *
  4.  * Extensive modifications by:  Tony Andrews       onecom!wldrdg!tony
  5.  * Turbo C 1.5 port by: Denny Muscatelli 061988
  6.  */
  7.  
  8. #include "stevie.h"
  9.  
  10. #include <regexp.h>    /* Henry Spencer's regular expression routines */
  11.  
  12. #ifdef    MEGAMAX
  13. overlay "search"
  14. #endif
  15.  
  16. /*
  17.  * This file contains various searching-related routines. These fall into
  18.  * three groups: string searches (for /, ?, n, and N), character searches
  19.  * within a single line (for f, F, t, T, etc), and "other" kinds of searches
  20.  * like the '%' command, and 'word' searches.
  21.  */
  22.  
  23. /*
  24.  * String searches
  25.  *
  26.  * The actual searches are done using Henry Spencer's regular expression
  27.  * library.
  28.  */
  29.  
  30. #define    BEGWORD    "([^a-zA-Z0-9_]|^)"    /* replaces "\<" in search strings */
  31. #define    ENDWORD    "([^a-zA-Z0-9_]|$)"    /* likewise replaces "\>" */
  32.  
  33. bool_t    begword;    /* does the search include a 'begin word' match */
  34.  
  35. /*
  36.  * mapstring(s) - map special backslash sequences
  37.  */
  38. static char *
  39. mapstring(s)
  40. register char    *s;
  41. {
  42.     static    char    ns[80];
  43.     char    *p;
  44.  
  45.     begword = FALSE;
  46.  
  47.     for (p = ns; *s ;s++) {
  48.         if (*s != '\\') {    /* not an escape */
  49.             *p++ = *s;
  50.             continue;
  51.         }
  52.         switch (*++s) {
  53.         case '/':
  54.             *p++ = '/';
  55.             break;
  56.  
  57.         case '<':
  58.             strcpy(p, BEGWORD);
  59.             p += strlen(BEGWORD);
  60.             begword = TRUE;
  61.             break;
  62.  
  63.         case '>':
  64.             strcpy(p, ENDWORD);
  65.             p += strlen(ENDWORD);
  66.             break;
  67.  
  68.         default:
  69.             *p++ = '\\';
  70.             *p++ = *s;
  71.             break;
  72.         }
  73.     }
  74.     *p++ = NUL;
  75.  
  76.     return ns;
  77. }
  78.  
  79. static char *laststr = NULL;
  80. static int lastsdir;
  81.  
  82. static LPTR *
  83. ssearch(dir,str)
  84. int    dir;    /* FORWARD or BACKWARD */
  85. char    *str;
  86. {
  87.     static LPTR *bcksearch(), *fwdsearch();
  88.     LPTR    *pos;
  89.  
  90.     if ( laststr != NULL )
  91.         free(laststr);
  92.     laststr = strsave(str);
  93.     lastsdir = dir;
  94.     if ( dir == BACKWARD )
  95.         pos = bcksearch(mapstring(str));
  96.     else
  97.         pos = fwdsearch(mapstring(str));
  98.  
  99.     /*
  100.      * This is kind of a kludge, but its needed to make
  101.      * 'beginning of word' searches land on the right place.
  102.      */
  103.     if (begword) {
  104.         if (pos->index != 0)
  105.             pos->index += 1;
  106.     }
  107.     return pos;
  108. }
  109.  
  110. void
  111. dosearch(dir,str)
  112. int dir;
  113. char *str;
  114. {
  115.     LPTR *p;
  116.  
  117.     if ((p = ssearch(dir,str)) == NULL)
  118.         msg("Pattern not found");
  119.     else {
  120.         LPTR savep;
  121.  
  122.         cursupdate();
  123.         /* if we're backing up, we make sure the line we're on */
  124.         /* is on the screen. */
  125.         setpcmark();
  126.         *Curschar = savep = *p;
  127.         cursupdate();
  128.     }
  129. }
  130.  
  131. #define    OTHERDIR(x)    (((x) == FORWARD) ? BACKWARD : FORWARD)
  132.  
  133. void
  134. repsearch(flag)
  135. int    flag;
  136. {
  137.     int    dir = lastsdir;
  138.  
  139.     if ( laststr == NULL )
  140.         beep();
  141.     else
  142.         dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
  143.  
  144.     lastsdir = dir;
  145. }
  146.  
  147. /*
  148.  * regerror - called by regexp routines when errors are detected.
  149.  */
  150. void
  151. regerror(s)
  152. char    *s;
  153. {
  154.     emsg(s);
  155. }
  156.  
  157. static LPTR *
  158. fwdsearch(str)
  159. register char *str;
  160. {
  161.     static LPTR infile;
  162.     register LPTR *p;
  163.     regexp *prog;
  164.     bool_t    want_start = (*str == '^');    /* looking for start of line? */
  165.  
  166.     register char *s;
  167.     register int i;
  168.  
  169.     if ((prog = regcomp(str)) == NULL) {
  170.         emsg("Invalid search string");
  171.         return NULL;
  172.     }
  173.  
  174.     p = Curschar;
  175.     i = Curschar->index + 1;
  176.     do {
  177.         s = p->linep->s + i;
  178.         i = 0;
  179.  
  180.         if (regexec(prog, s)) {        /* got a match */
  181.             /*
  182.              * If we wanted the start of a line and we aren't
  183.              * really there, then a match doesn't count.
  184.              */
  185.             if (want_start && (s != p->linep->s))
  186.                 continue;
  187.  
  188.             infile.linep = p->linep;
  189.             infile.index = (int) (prog->startp[0] - p->linep->s);
  190.             free(prog);
  191.             return (&infile);
  192.         }
  193.     } while ((p = nextline(p)) != NULL);
  194.  
  195.     /*
  196.      * If wrapscan isn't set, then don't scan from the beginning
  197.      * of the file. Just return failure here.
  198.      */
  199.     if (!P(P_WS)) {
  200.         free(prog);
  201.         return NULL;
  202.     }
  203.  
  204.     /* search from the beginning of the file to Curschar */
  205.     for (p = Filemem; p != NULL ;p = nextline(p)) {
  206.         s = p->linep->s;
  207.  
  208.         if (regexec(prog, s)) {        /* got a match */
  209.             infile.linep = p->linep;
  210.             infile.index = (int) (prog->startp[0] - s);
  211.             free(prog);
  212.             return (&infile);
  213.         }
  214.  
  215.         if (p->linep == Curschar->linep)
  216.             break;
  217.     }
  218.  
  219.     free(prog);
  220.     return(NULL);
  221. }
  222.  
  223. static LPTR *
  224. bcksearch(str)
  225. char *str;
  226. {
  227.     static LPTR infile;
  228.     register LPTR *p;
  229.     regexp    *prog;
  230.     register char *s;
  231.     register int i;
  232.     bool_t    want_start = (*str == '^');    /* looking for start of line? */
  233.     register char    *match;
  234.  
  235.     /* make sure str isn't empty */
  236.     if (str == NULL || *str == NUL)
  237.         return NULL;
  238.  
  239.     if ((prog = regcomp(str)) == NULL) {
  240.         emsg("Invalid search string");
  241.         return NULL;
  242.     }
  243.  
  244.     p = Curschar;
  245.     dec(p);
  246.  
  247.     if (begword)        /* so we don't get stuck on one match */
  248.         dec(p);
  249.  
  250.     i = (want_start) ? 0 : p->index;
  251.  
  252.     do {
  253.         s = p->linep->s;
  254.  
  255.         if (regexec(prog, s)) {        /* match somewhere on line */
  256.  
  257.             if (want_start) {    /* could only have been one */
  258.                 infile.linep = p->linep;
  259.                 infile.index = (int) (prog->startp[0] - s);
  260.                 free(prog);
  261.                 return (&infile);
  262.             }
  263.  
  264.             /*
  265.              * Now, if there are multiple matches on this line,
  266.              * we have to get the last one. Or the last one
  267.              * before the cursor, if we're on that line.
  268.              */
  269.  
  270.             match = prog->startp[0];
  271.  
  272.             while (regexec(prog, prog->endp[0])) {
  273.                 if ((i >= 0) && ((prog->startp[0] - s) > i))
  274.                     break;
  275.                 match = prog->startp[0];
  276.             }
  277.  
  278.             if ((i >= 0) && ((match - s) > i)) {
  279.                 i = -1;
  280.                 continue;
  281.             }
  282.  
  283.             infile.linep = p->linep;
  284.             infile.index = (int) (match - s);
  285.             free(prog);
  286.             return (&infile);
  287.         }
  288.         i = -1;
  289.  
  290.     } while ((p = prevline(p)) != NULL);
  291.  
  292.     /*
  293.      * If wrapscan isn't set, bag the search now
  294.      */
  295.     if (!P(P_WS)) {
  296.         free(prog);
  297.         return NULL;
  298.     }
  299.  
  300.     /* search backward from the end of the file */
  301.     p = prevline(Fileend);
  302.     do {
  303.         s = p->linep->s;
  304.  
  305.         if (regexec(prog, s)) {        /* match somewhere on line */
  306.  
  307.             if (want_start) {    /* could only have been one */
  308.                 infile.linep = p->linep;
  309.                 infile.index = (int) (prog->startp[0] - s);
  310.                 free(prog);
  311.                 return (&infile);
  312.             }
  313.  
  314.             /*
  315.              * Now, if there are multiple matches on this line,
  316.              * we have to get the last one.
  317.              */
  318.  
  319.             match = prog->startp[0];
  320.  
  321.             while (regexec(prog, prog->endp[0]))
  322.                 match = prog->startp[0];
  323.  
  324.             infile.linep = p->linep;
  325.             infile.index = (int) (match - s);
  326.             free(prog);
  327.             return (&infile);
  328.         }
  329.  
  330.         if (p->linep == Curschar->linep)
  331.             break;
  332.  
  333.     } while ((p = prevline(p)) != NULL);
  334.  
  335.     free(prog);
  336.     return NULL;
  337. }
  338.  
  339. /*
  340.  * Character Searches
  341.  */
  342.  
  343. static char lastc = NUL;    /* last character searched for */
  344. static int  lastcdir;        /* last direction of character search */
  345. static int  lastctype;        /* last type of search ("find" or "to") */
  346.  
  347. /*
  348.  * searchc(c, dir, type)
  349.  *
  350.  * Search for character 'c', in direction 'dir'. If type is 0, move to
  351.  * the position of the character, otherwise move to just before the char.
  352.  */
  353. bool_t
  354. searchc(c, dir, type)
  355. char    c;
  356. int    dir;
  357. int    type;
  358. {
  359.     LPTR    save;
  360.  
  361.     save = *Curschar;    /* save position in case we fail */
  362.     lastc = c;
  363.     lastcdir = dir;
  364.     lastctype = type;
  365.  
  366.     /*
  367.      * On 'to' searches, skip one to start with so we can repeat
  368.      * searches in the same direction and have it work right.
  369.      */
  370.     if (type)
  371.         (dir == FORWARD) ? oneright() : oneleft();
  372.  
  373.     while ( (dir == FORWARD) ? oneright() : oneleft() ) {
  374.         if (gchar(Curschar) == c) {
  375.             if (type)
  376.                 (dir == FORWARD) ? oneleft() : oneright();
  377.             return TRUE;
  378.         }
  379.     }
  380.     *Curschar = save;
  381.     return FALSE;
  382. }
  383.  
  384. bool_t
  385. crepsearch(flag)
  386. int    flag;
  387. {
  388.     int    dir = lastcdir;
  389.     int    rval;
  390.  
  391.     if (lastc == NUL)
  392.         return FALSE;
  393.  
  394.     rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
  395.  
  396.     lastcdir = dir;        /* restore dir., since it may have changed */
  397.  
  398.     return rval;
  399. }
  400.  
  401. /*
  402.  * "Other" Searches
  403.  */
  404.  
  405. /*
  406.  * showmatch - move the cursor to the matching paren or brace
  407.  */
  408. LPTR *
  409. showmatch()
  410. {
  411.     static    LPTR    pos;
  412.     int    (*move)(), inc(), dec();
  413.     char    initc = gchar(Curschar);    /* initial char */
  414.     char    findc;                /* terminating char */
  415.     char    c;
  416.     int    count = 0;
  417.  
  418.     pos = *Curschar;        /* set starting point */
  419.  
  420.     switch (initc) {
  421.  
  422.     case '(':
  423.         findc = ')';
  424.         move = inc;
  425.         break;
  426.     case ')':
  427.         findc = '(';
  428.         move = dec;
  429.         break;
  430.     case '{':
  431.         findc = '}';
  432.         move = inc;
  433.         break;
  434.     case '}':
  435.         findc = '{';
  436.         move = dec;
  437.         break;
  438.     case '[':
  439.         findc = ']';
  440.         move = inc;
  441.         break;
  442.     case ']':
  443.         findc = '[';
  444.         move = dec;
  445.         break;
  446.     default:
  447.         return (LPTR *) NULL;
  448.     }
  449.  
  450.     while ((*move)(&pos) != -1) {        /* until end of file */
  451.         c = gchar(&pos);
  452.         if (c == initc)
  453.             count++;
  454.         else if (c == findc) {
  455.             if (count == 0)
  456.                 return &pos;
  457.             count--;
  458.         }
  459.     }
  460.     return (LPTR *) NULL;            /* never found it */
  461. }
  462.  
  463. /*
  464.  * findfunc(dir) - Find the next function in direction 'dir'
  465.  *
  466.  * Return TRUE if a function was found.
  467.  */
  468. bool_t
  469. findfunc(dir)
  470. int    dir;
  471. {
  472.     LPTR    *curr;
  473.  
  474.     curr = Curschar;
  475.  
  476.     do {
  477.         curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
  478.  
  479.         if (curr != NULL && curr->linep->s[0] == '{') {
  480.             setpcmark();
  481.             *Curschar = *curr;
  482.             return TRUE;
  483.         }
  484.     } while (curr != NULL);
  485.  
  486.     return FALSE;
  487. }
  488.  
  489. /*
  490.  * The following routines do the word searches performed by the
  491.  * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
  492.  */
  493.  
  494. /*
  495.  * To perform these searches, characters are placed into one of three
  496.  * classes, and transitions between classes determine word boundaries.
  497.  *
  498.  * The classes are:
  499.  *
  500.  * 0 - white space
  501.  * 1 - letters, digits, and underscore
  502.  * 2 - everything else
  503.  */
  504.  
  505. static    int    stype;        /* type of the word motion being performed */
  506.  
  507. #define    C0(c)    (((c) == ' ') || ((c) == '\t') || ((c) == NUL))
  508. #define    C1(c)    (isalpha(c) || isdigit(c) || ((c) == '_'))
  509.  
  510. /*
  511.  * cls(c) - returns the class of character 'c'
  512.  *
  513.  * The 'type' of the current search modifies the classes of characters
  514.  * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
  515.  * class 2 are reported as class 1 since only white space boundaries are
  516.  * of interest.
  517.  */
  518. static    int
  519. cls(c)
  520. char    c;
  521. {
  522.     if (C0(c))
  523.         return 0;
  524.  
  525.     if (C1(c))
  526.         return 1;
  527.  
  528.     /*
  529.      * If stype is non-zero, report these as class 1.
  530.      */
  531.     return (stype == 0) ? 2 : 1;
  532. }
  533.  
  534.  
  535. /*
  536.  * fwd_word(pos, type) - move forward one word
  537.  *
  538.  * Returns the resulting position, or NULL if EOF was reached.
  539.  */
  540. LPTR *
  541. fwd_word(p, type)
  542. LPTR    *p;
  543. int    type;
  544. {
  545.     static    LPTR    pos;
  546.     int    sclass = cls(gchar(p));        /* starting class */
  547.  
  548.     pos = *p;
  549.  
  550.     stype = type;
  551.  
  552.     /*
  553.      * We always move at least one character.
  554.      */
  555.     if (inc(&pos) == -1)
  556.         return NULL;
  557.  
  558.     if (sclass != 0) {
  559.         while (cls(gchar(&pos)) == sclass) {
  560.             if (inc(&pos) == -1)
  561.                 return NULL;
  562.         }
  563.         /*
  564.          * If we went from 1 -> 2 or 2 -> 1, return here.
  565.          */
  566.         if (cls(gchar(&pos)) != 0)
  567.             return &pos;
  568.     }
  569.  
  570.     /* We're in white space; go to next non-white */
  571.  
  572.     while (cls(gchar(&pos)) == 0) {
  573.         /*
  574.          * We'll stop if we land on a blank line
  575.          */
  576.         if (pos.index == 0 && pos.linep->s[0] == NUL)
  577.             break;
  578.  
  579.         if (inc(&pos) == -1)
  580.             return NULL;
  581.     }
  582.  
  583.     return &pos;
  584. }
  585.  
  586. /*
  587.  * bck_word(pos, type) - move backward one word
  588.  *
  589.  * Returns the resulting position, or NULL if EOF was reached.
  590.  */
  591. LPTR *
  592. bck_word(p, type)
  593. LPTR    *p;
  594. int    type;
  595. {
  596.     static    LPTR    pos;
  597.     int    sclass = cls(gchar(p));        /* starting class */
  598.  
  599.     pos = *p;
  600.  
  601.     stype = type;
  602.  
  603.     if (dec(&pos) == -1)
  604.         return NULL;
  605.  
  606.     /*
  607.      * If we're in the middle of a word, we just have to
  608.      * back up to the start of it.
  609.      */
  610.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  611.         /*
  612.          * Move backward to start of the current word
  613.          */
  614.         while (cls(gchar(&pos)) == sclass) {
  615.             if (dec(&pos) == -1)
  616.                 return NULL;
  617.         }
  618.         inc(&pos);            /* overshot - forward one */
  619.         return &pos;
  620.     }
  621.  
  622.     /*
  623.      * We were at the start of a word. Go back to the start
  624.      * of the prior word.
  625.      */
  626.  
  627.     while (cls(gchar(&pos)) == 0) {        /* skip any white space */
  628.         /*
  629.          * We'll stop if we land on a blank line
  630.          */
  631.         if (pos.index == 0 && pos.linep->s[0] == NUL)
  632.             return &pos;
  633.  
  634.         if (dec(&pos) == -1)
  635.             return NULL;
  636.     }
  637.  
  638.     sclass = cls(gchar(&pos));
  639.  
  640.     /*
  641.      * Move backward to start of this word.
  642.      */
  643.     while (cls(gchar(&pos)) == sclass) {
  644.         if (dec(&pos) == -1)
  645.             return NULL;
  646.     }
  647.     inc(&pos);            /* overshot - forward one */
  648.  
  649.     return &pos;
  650. }
  651.  
  652. /*
  653.  * end_word(pos, type) - move to the end of the word
  654.  *
  655.  * There is an apparent bug in the 'e' motion of the real vi. At least
  656.  * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
  657.  * the 'e' motion crosses blank lines. When the real vi crosses a blank
  658.  * line in an 'e' motion, the cursor is placed on the FIRST character
  659.  * of the next non-blank line. The 'E' command, however, works correctly.
  660.  * Since this appears to be a bug, I have not duplicated it here.
  661.  *
  662.  * Returns the resulting position, or NULL if EOF was reached.
  663.  */
  664. LPTR *
  665. end_word(p, type)
  666. LPTR    *p;
  667. int    type;
  668. {
  669.     static    LPTR    pos;
  670.     int    sclass = cls(gchar(p));        /* starting class */
  671.  
  672.     pos = *p;
  673.  
  674.     stype = type;
  675.  
  676.     if (inc(&pos) == -1)
  677.         return NULL;
  678.  
  679.     /*
  680.      * If we're in the middle of a word, we just have to
  681.      * move to the end of it.
  682.      */
  683.     if (cls(gchar(&pos)) == sclass && sclass != 0) {
  684.         /*
  685.          * Move forward to end of the current word
  686.          */
  687.         while (cls(gchar(&pos)) == sclass) {
  688.             if (inc(&pos) == -1)
  689.                 return NULL;
  690.         }
  691.         dec(&pos);            /* overshot - forward one */
  692.         return &pos;
  693.     }
  694.  
  695.     /*
  696.      * We were at the end of a word. Go to the end
  697.      * of the next word.
  698.      */
  699.  
  700.     while (cls(gchar(&pos)) == 0) {        /* skip any white space */
  701.         if (inc(&pos) == -1)
  702.             return NULL;
  703.     }
  704.  
  705.     sclass = cls(gchar(&pos));
  706.  
  707.     /*
  708.      * Move forward to end of this word.
  709.      */
  710.     while (cls(gchar(&pos)) == sclass) {
  711.         if (inc(&pos) == -1)
  712.             return NULL;
  713.     }
  714.     dec(&pos);            /* overshot - forward one */
  715.  
  716.     return &pos;
  717. }
  718.